package nborm;

import android.graphics.Bitmap;
import android.text.TextUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import nborm.annotation.Ignore;
import nborm.annotation.TableId;
import nborm.annotation.Unique;

/**
 * 数据库表帮助类。
 */
public class TableHelper {

    private static final String TAG = "TableHelper";

    /**
     * 缓存表信息。
     */
    private static Map<Class, TableInfo> sTableInfoMap = new ConcurrentHashMap<>();

    /**
     * 类型映射
     */
    private static Map<Class, String> sTypeMap = new ConcurrentHashMap<>();

    /**
     * 数据类型存储的时候全部转成字符串。
     */
    static {
        sTypeMap.put(byte.class, "TEXT");
        sTypeMap.put(Byte.class, "TEXT");
        sTypeMap.put(int.class, "TEXT");
        sTypeMap.put(Integer.class, "TEXT");
        sTypeMap.put(short.class, "TEXT");
        sTypeMap.put(Short.class, "TEXT");
        sTypeMap.put(long.class, "TEXT");
        sTypeMap.put(Long.class, "TEXT");
        sTypeMap.put(float.class, "TEXT");
        sTypeMap.put(Float.class, "TEXT");
        sTypeMap.put(double.class, "TEXT");
        sTypeMap.put(Double.class, "TEXT");
        sTypeMap.put(char.class, "TEXT");
        sTypeMap.put(Character.class, "TEXT");
        sTypeMap.put(Boolean.class, "TEXT");
        sTypeMap.put(boolean.class, "TEXT");
        sTypeMap.put(String.class, "TEXT");
        sTypeMap.put(Bitmap.class, "TEXT");
    }

    public static boolean isNumber(Class<?> cls) {
        return cls == int.class
                || cls == Integer.class
                || cls == long.class
                || cls == Long.class
                || cls == float.class
                || cls == Float.class
                || cls == double.class
                || cls == Double.class
                || cls == byte.class
                || cls == Byte.class
                || cls == short.class
                || cls == Short.class;
    }

    /**
     * 生成建表语句。
     *
     * @param object
     * @return
     */
    public static String getCreateTableSql(Class<?> object) {
        TableInfo tableInfo = getTableInfo(object);
        return tableInfo.getCreateTableSql();
    }

    /**
     * 根据类型获取字段映射
     *
     * @param cls
     * @return
     */
    public static TableInfo getTableInfo(Class<?> cls) {
        TableInfo info = sTableInfoMap.get(cls);
        if (info == null) {
            info = new TableInfo();
            info.setTableName(cls.getSimpleName());

            Map<String, Field> map = new HashMap<>();

            Field[] fields = cls.getDeclaredFields();
            Field[] superFields = cls.getSuperclass() == null ? new Field[0] : cls.getSuperclass().getDeclaredFields();
            Field[] newFields = new Field[fields.length + superFields.length];
            System.arraycopy(fields, 0, newFields, 0, fields.length);
            System.arraycopy(superFields, 0, newFields, fields.length, superFields.length);

            //建表语句sql
            StringBuilder builder = new StringBuilder();
            builder.append("create table if not exists ")
                    .append(info.getTableName())
                    .append(" (");

            for (int i = 0; i < newFields.length; i++) {
                Field field = newFields[i];
                field.setAccessible(true);
                int modifiers = field.getModifiers();
                String fieldName = field.getName();
                Ignore ignore = field.getAnnotation(Ignore.class);
                if (ignore != null) {
                    continue;
                }
                //final,static,transient,将会忽略，字段名称为$change和serialVersionUID也会忽略。
                if (Modifier.isFinal(modifiers)
                        || Modifier.isStatic(modifiers)
                        || Modifier.isTransient(modifiers)
                        || "$change".equals(fieldName)
                        || "serialVersionUID".equals(fieldName)) {
                    if (i == newFields.length - 1) {
                        if (builder.charAt(builder.length() - 1) == ',') {
                            builder.deleteCharAt(builder.length() - 1);
                        }
                    }
                    continue;
                }
                //必须为表指明一个主键，使用TableId注解来指明，默认是自增的。
                TableId anoTableId = field.getAnnotation(TableId.class);
                if (anoTableId != null) {
                    info.setTableId(field.getName());
                    info.setAutoincrement(anoTableId.autoincrement());
                }

                //字段是否是唯一的。
                Unique unique = field.getAnnotation(Unique.class);
                if (unique != null) {
                    info.setUniqueField(field);
                }

                //类型转换注解。
                nborm.annotation.TypeConverter typeConverter = field.getAnnotation(nborm.annotation.TypeConverter.class);
                if (typeConverter != null) {
                    try {
                        info.getTypeConverterMap().put(fieldName, (android.animation.TypeConverter)
                                Class.forName(typeConverter.coverClassName()).newInstance());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                builder.append(getCreateColumnParam(field));
                if (i < newFields.length - 1) {
                    builder.append(",");
                }
                map.put(field.getName(), field);
            }
            builder.append(")");
            info.setCreateTableSql(builder.toString());
            info.setColumns(map);
            sTableInfoMap.put(cls, info);
        }
        if (TextUtils.isEmpty(info.getTableId())) {
            throw new IllegalArgumentException("请用@TableId注解来标明主键:" + cls);
        }
        return info;
    }

    /**
     * 生成建表语句的参数。
     *
     * @param field
     * @return
     */
    private static String getCreateColumnParam(Field field) {
        TableId tableId = field.getAnnotation(TableId.class);
        if (tableId != null) {
            if (tableId.autoincrement()) {
                return field.getName() + " INTEGER primary key autoincrement";
            } else {
                return field.getName() + " " + getType(field) + " primary key";
            }
        } else {
            return field.getName() + " " + getType(field);
        }
    }

    /**
     * 字段类型。
     *
     * @param field
     * @return
     */
    public static String getType(Field field) {
        //数据库已修改为只存储字符串类型。
        return "TEXT";
    }

    /**
     * 是否是非基本类型。
     *
     * @param field
     * @return
     */
    public static boolean isCustomField(Field field) {
        return !sTypeMap.containsKey(field.getType());
    }
}
